{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0f39e9b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generating gradient descent animations for different learning rates...\n",
      "\n",
      "[✓] GIF saved: /Users/kchen/Desktop/Project/CIOLLM/tasks/translate/data/output/courses/ACP/p2_Build LLM Q&A System/resources/2_7/gradient_descent_lr_0_99.gif\n",
      "[✓] GIF saved: /Users/kchen/Desktop/Project/CIOLLM/tasks/translate/data/output/courses/ACP/p2_Build LLM Q&A System/resources/2_7/gradient_descent_lr_0_9.gif\n",
      "[✓] GIF saved: /Users/kchen/Desktop/Project/CIOLLM/tasks/translate/data/output/courses/ACP/p2_Build LLM Q&A System/resources/2_7/gradient_descent_lr_0_3.gif\n",
      "[✓] GIF saved: /Users/kchen/Desktop/Project/CIOLLM/tasks/translate/data/output/courses/ACP/p2_Build LLM Q&A System/resources/2_7/gradient_descent_lr_0_1.gif\n",
      "[✓] GIF saved: /Users/kchen/Desktop/Project/CIOLLM/tasks/translate/data/output/courses/ACP/p2_Build LLM Q&A System/resources/2_7/gradient_descent_lr_0_01.gif\n",
      "\n",
      "✅ All animations generated!\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.animation import FuncAnimation\n",
    "import os\n",
    "\n",
    "def create_gradient_descent_gif(learning_rate, gif_path='gradient_descent.gif', frames=30):\n",
    "    \"\"\"\n",
    "    Creates an animated GIF showing gradient descent on a quadratic cost function J(a) = (a - 3)^2 + 1.\n",
    "    Demonstrates how learning rate affects convergence, oscillation, or divergence.\n",
    "\n",
    "    Parameters:\n",
    "    - learning_rate: Step size multiplier (critical for convergence)\n",
    "    - gif_path: Output file path for the GIF\n",
    "    - frames: Number of iterations to simulate\n",
    "    \"\"\"\n",
    "    # Define the cost function and its gradient\n",
    "    def cost_function(a):\n",
    "        return (a - 3)**2 + 1  # Convex quadratic, minimum at a = 3\n",
    "\n",
    "    def gradient(a):\n",
    "        return 2 * (a - 3)  # Derivative: slope at point a\n",
    "\n",
    "    # Initial parameter value (start far from minimum)\n",
    "    a_initial = 13.0\n",
    "    a = a_initial\n",
    "    a_min = 3.0\n",
    "    J_min = cost_function(a_min)\n",
    "\n",
    "    # Track optimization path\n",
    "    a_history = [a]\n",
    "    J_history = [cost_function(a)]\n",
    "\n",
    "    # Perform gradient descent\n",
    "    for _ in range(frames - 1):\n",
    "        grad = gradient(a)\n",
    "        a = a - learning_rate * grad  # Update rule\n",
    "        a_history.append(a)\n",
    "        J_history.append(cost_function(a))\n",
    "\n",
    "    # Convert to arrays\n",
    "    a_history = np.array(a_history)\n",
    "    J_history = np.array(J_history)\n",
    "\n",
    "    # Generate smooth curve for background\n",
    "    a_vals = np.linspace(-7, 13, 500)\n",
    "    J_vals = cost_function(a_vals)\n",
    "\n",
    "    # Setup plot\n",
    "    fig, ax = plt.subplots(figsize=(10, 6))\n",
    "    ax.plot(a_vals, J_vals, label='Cost Function $J(a)$', color='blue', linewidth=2)\n",
    "    point_current, = ax.plot([], [], 'o', color='yellow', markersize=12, label='Current Position')\n",
    "    point_minimum, = ax.plot([a_min], [J_min], 'o', color='red', markersize=10, label='Minimum Point')\n",
    "\n",
    "    # Text box for dynamic info\n",
    "    text = ax.text(\n",
    "        0.4, 0.75, '', transform=ax.transAxes, fontsize=12,\n",
    "        bbox=dict(boxstyle=\"round,pad=0.5\", facecolor=\"wheat\", edgecolor=\"black\", alpha=0.9)\n",
    "    )\n",
    "\n",
    "    # Axis labels and title\n",
    "    ax.set_xlabel('$a$', fontsize=14)\n",
    "    ax.set_ylabel('$J(a)$', fontsize=14)\n",
    "    ax.set_title(f'Gradient Descent: Learning Rate = {learning_rate}', fontsize=16, fontweight='bold')\n",
    "    ax.legend(loc='upper center', fontsize=12)\n",
    "    ax.grid(True, linestyle='--', alpha=0.6)\n",
    "    ax.set_ylim(0, 120)  # Fixed y-limits for consistent animation\n",
    "    ax.set_xlim(-7, 13)\n",
    "\n",
    "    # Animation update function\n",
    "    def update(frame):\n",
    "        point_current.set_data([a_history[frame]], [J_history[frame]])\n",
    "        text.set_text(\n",
    "            f'Iteration: {frame + 1}\\n'\n",
    "            f'Learning Rate: {learning_rate}\\n'\n",
    "            f'a = {a_history[frame]:.3f}\\n'\n",
    "            f'J(a) = {J_history[frame]:.3f}'\n",
    "        )\n",
    "        return point_current, text\n",
    "\n",
    "    # Create animation\n",
    "    ani = FuncAnimation(fig, update, frames=frames, interval=400, blit=True, repeat=False)\n",
    "\n",
    "    # Save as GIF\n",
    "    ani.save(gif_path, writer='pillow', fps=2.5)\n",
    "    plt.close(fig)\n",
    "    print(f\"[✓] GIF saved: {os.path.abspath(gif_path)}\")\n",
    "\n",
    "# ======================\n",
    "# Generate GIFs for Different Learning Rates\n",
    "# ======================\n",
    "\n",
    "learning_rates = [0.99, 0.9, 0.3, 0.1, 0.01]\n",
    "\n",
    "print(\"Generating gradient descent animations for different learning rates...\\n\")\n",
    "for lr in learning_rates:\n",
    "    filename = f'gradient_descent_lr_{str(lr).replace(\".\", \"_\")}.gif'\n",
    "    create_gradient_descent_gif(lr, filename, frames=30)\n",
    "\n",
    "print(\"\\n✅ All animations generated!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea6ec41d",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llm_learn",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
